hbase预分区(HBase调优:预分区与行键设计) 您所在的位置:网站首页 hbase startrowkey hbase预分区(HBase调优:预分区与行键设计)

hbase预分区(HBase调优:预分区与行键设计)

#hbase预分区(HBase调优:预分区与行键设计)| 来源: 网络整理| 查看: 265

本文目录HBase调优:预分区与行键设计【HBase】HBase 自动拆分和预分区Hbase分区HBase写数据的异常问题以及优化什么是hbase中的散列和预分区hbase scan的startRow和endRowhbase建很多空表对集群有影响么hbase预分区与region切割的关系hbase 的数据存储及Region变化(flush compaction spilt)和性能调优Hbase读写原理HBase调优:预分区与行键设计

基于此我们可以控制在建表的时候,创建多个空region,并确定每个region的起始和终止rowkey,这样只要我们的rowkey设计能均匀的命中各个region,就不会存在写热点问题。自然split的几率也会大大降低。当然随着数据量的不断增长,该split的还是要进行split。像这样预先创建hbase表分区的方式,称之为预分区。

hash(主键) + 年月日时(2019062315) 这里只取hash(主键)的前6位,使得行键的长度正好是16,也就是8的整数倍,在64位计算机中,效果最好。

列族固定,只有一个,设为f,标签为分钟加上秒数: 分秒(5623) 如果需要精确到毫秒,可以为列族f设置有多个版本或者将标签设计为分秒毫秒(5623142)或者分秒.版本号(5623.1)

一个regionserver可以管理的region数量和列族数量与每个列族缓存的大小有关,计算公式如下:

我这里只分了三个region,用hbase shell命令创建表,设置预分区数量为3

下图中,可以看到,预分区以后,数据的读写访问请求数量均匀分布在3台RegionServer上,避免了热点问题。

【HBase】HBase 自动拆分和预分区

HBase 中,表会被划分为1...n 个 Region,被托管在 RegionServer 中。

Region 二个重要的属性:StartKey 与 EndKey 表示这个 Region 维护的 RowKey 范围,当读/写数据时,如果 RowKey 落在某个 start-end key 范围内,那么就会定位到目标region并且读/写到相关的数据。

默认,HBase 在创建表的时候,会自动为表分配一个 Region,正处于混沌时期,start-end key 无边界,所有 RowKey 都往这个 Region里分配。

当数据越来越多,Region 的 size 越来越大时,达到默认的阈值时(根据不同的拆分策略有不同的阈值),HBase 中该 Region 将会进行 split,会找到一个 MidKey 将 Region 一分为二,成为 2 个 Region。而 MidKey 则为这二个 Region 的临界,左为 N 无下界,右为 M 无上界。《 MidKey 被分配到 N 区,》 MidKey 则会被分配到 M 区。

随着数据量进一步扩大,分裂的两个 Region 达到临界后将重复前面的过程,分裂出更多的 Region。

Region 的分割操作是不可见的,Master 不会参与其中。RegionServer 拆分 Region的步骤是:先将该 Region 下线,然后拆分,将其子 Region 加入到 META 元信息中,再将他们加入到原本的 RegionServer 中,最后汇报 Master。

执行 split 的线程是 CompactSplitThread。

在 2.0.5 版本中,HBase 提供了 7 种自动拆分策略:

他们之间的继承关系如下:

有三种配置方法:

0.94.0 之前的默认拆分策略,这种策略非常简单,只要 Region 中的任何一个 StoreFile 的大小达到了 hbase.hregion.max.filesize 所定义的大小 ,就进行拆分。

1)相关参数:

hbase.hregion.max.filesize

2)部分源码 :

拆分的阈值大小可在创建表的时候设置,如果没有设置,就取 hbase.hregion.max.filesize 这个配置定义的值,如果这个配置也没有定义,取默认值 10G。

3)拆分效果:

经过这种策略的拆分后,Region 的大小是均匀的,例如一个 10G 的Region,拆分为两个 Region 后,这两个新的 Region 的大小是相差不大的,理想状态是每个都是5G。

**ConstantSizeRegionSplitPolicy **切分策略对于大表和小表没有明显的区分,阈值(hbase.hregion.max.filesize):

4)创建表时配置:

该策略继承自 ConstantSizeRegionSplitPolicy,是 0.94.0 到 2.0.0 版本的默认策略,其 优化了原来 ConstantSizeRegionSplitPolicy 只是单一按照 Region 文件大小的拆分策略,增加了对当前表的分片数作为判断因子 。当Region中某个 Store Size 达到 sizeToCheck 阀值时进行拆分,sizeToCheck 计算如下:

如果表的分片数为 0 或者大于 100,则切分大小还是以设置的单一 Region 文件大小为标准。如果分片数在 1~99 之间,则由 min(单一 Region 大小, Region 增加策略的初始化大小 * 当前 Table Region 数的3次方) 决定 。

Region 增加策略的初始化大小计算如下:

1)相关参数:

hbase.hregion.max.filesize

hbase.increasing.policy.initial.size

hbase.hregion.memstore.flush.size

2)部分源码:

在默认情况,使用IncreasingToUpperBoundRegionSplitPolicy 策略拆分 Region 的过程是:

3)拆分效果:

和 ConstantSizeRegionSplitPolicy 一样,也是均匀拆分。

不同的是, IncreasingToUpperBoundRegionSplitPolicy 切分策略弥补了ConstantSizeRegionSplitPolicy 的短板,能够自适应大表和小表,并且在大集群条件下对于很多大表来说表现很优秀。

但并不完美,这种策略下很多小表会在大集群中产生大量小 Region,分散在整个集群中。而且在发生 Region 迁移时也可能会触发 Region 分裂。

4)创建表时配置:

2.0 版本默认切分策略。SteppingSplitPolicy 是IncreasingToUpperBoundRegionSplitPolicy 的子类,其对 Region 拆分文件大小做了优化,如果只有1个 Region 的情况下,那第1次的拆分就是 256M,后续则按配置的拆分文件大小(10G)做为拆分标准。

1)相关参数:

同 IncreasingToUpperBoundRegionSplitPolicy 。

2)全部源码:

它的源码只有一个方法,优化了 getSizeToCheck 方法,其他都是继承 自IncreasingToUpperBoundRegionSplitPolicy 类。

3)拆分效果:

在 IncreasingToUpperBoundRegionSplitPolicy 策略中,针对大表的拆分表现很不错,但是针对小表会产生过多的 Region,SteppingSplitPolicy 则将小表的 Region 控制在一个合理的范围,对大表的拆分也不影响。

4)创建表时配置:

KeyPrefixRegionSplitPolicy 是 IncreasingToUpperBoundRegionSplitPolicy 的子类,该策略除了具备其父类自动调整 Region 拆分阈值大小、适应大小表的特点外,增加了对拆分点(splitPoint,拆分点就是 Region 被拆分处的 RowKey)的定义,可以保证有相同前缀的 RowKey不会被拆分到两个不同的 Region 里面。

1)相关参数:

在 IncreasingToUpperBoundRegionSplitPolicy 的配置之上增加了一个参数。

KeyPrefixRegionSplitPolicy.prefix_length

2)部分源码:

先从父类获取拆分点,如果设置了 prefixLength 》 0,就从父类拆分点中截取需要的前缀作为新的拆分点返回。

3)拆分效果:

KeyPrefixRegionSplitPolicy (SteppingSplitPolicy、DelimitedKeyPrefixRegionSplitPolicy、BusyRegionSplitPolicy (HBase-2.x Only))按照 RowKey 的前缀去拆分 Region,但是什么时候拆分,原 Region 容量的最大值是多少还是需要使用 IncreasingToUpperBoundRegionSplitPolicy 的方法去计算 。

如果所有数据都只有一两个前缀,那么采用默认的策略较好。 如果前缀划分的比较细,查询就比较容易发生跨 Region 查询的情况,此时采用KeyPrefixRegionSplitPolicy 较好。

所以这个策略适用的场景是:

4)创建表时配置:

继承自 IncreasingToUpperBoundRegionSplitPolicy,也是根据 RowKey 前缀来进行拆分的。不同就是:KeyPrefixRegionSplitPolicy 是根据 RowKey 的固定前几位字符来进行判断,而 DelimitedKeyPrefixRegionSplitPolicy 是根据分隔符来判断的。

1)相关参数:

在 IncreasingToUpperBoundRegionSplitPolicy 的配置之上增加了一个参数。

DelimitedKeyPrefixRegionSplitPolicy.delimiter

2)部分源码:

先找到分隔符下标位置,然后从父类的拆分点截取出来。

3)拆分效果:

DelimitedKeyPrefixRegionSplitPolicy 根据 RowKey 中指定分隔字符做为拆分,显得更加灵活,如 RowKey 的值为“userid_eventtype_eventid”,userId 不是定长的,则 DelimitedKeyPrefixRegionSplitPolicy 可以取 RowKey 值中从左往右且第一个分隔字符串之前的字符做为拆分串,在该示例中就是“userid”。

4)创建表时配置:

之前的策略都未考虑 Region 热点问题,考虑某些 Region 可能被频繁访问,负荷很大,BusyRegionSplitPolicy 策略同样继承自 IncreasingToUpperBoundRegionSplitPolicy,但主要针对 Region 问题,是在 2.x 中新增加的拆分策略。

1)相关参数:

在 IncreasingToUpperBoundRegionSplitPolicy 的配置之上增加了如下参数:

hbase.busy.policy.blockedRequests

hbase.busy.policy.minAge

hbase.busy.policy.aggWindow

2)部分源码:

在判断是否需要进行拆分的时候,先调用父类的 shouldSplit 方法检验,如果需要则直接返回 true,否则需要判断当前时间是否比开始时间大于 minAge 值,如果是的,则计算请求阻塞率 blockedReqRate,如果阻塞率大于设定的阈值,则进行拆分。

阻塞率的计算如下:

主要的计算逻辑是:请求的被阻塞率(aggBlockedRate) = curTime - prevTime 时间内新增的阻塞请求 / 这段时间的总请求。

3)拆分效果:

如果系统常常会出现热点 Region,又对性能有很高的追求,那么这种策略可能会比较适合。

它会通过拆分热点 Region 来缓解热点 Region 的压力,但是根据热点来拆分Region 也会带来很多不确定性因素,因为不能确定下一个被拆分的 Region 是哪个。

4)创建表时配置:

DisabledRegionSplitPolicy 就是不使用 Region 拆分策略,将所有的数据都写到同一个 Region 中。

1)全部源码:

源码很简单,就是直接返回 false。

2)拆分效果:

这个策略极少使用。

即使在建表的时候合理的进行了预拆分,还没有写入的数据的时候就已经手动分好了 Region,但是随着数据的持续写入,我预先分好的 Region 的大小也会达到阈值,那时候还是要依靠 HBase 的自动拆分策略去拆分 Region。

但这种策略也有它的用途:

假如有一批静态数据,一次存入以后不会再加入新数据,且这批数据主要是用于查询,为了性能好一些,可以先进行预分区后,各个 Region 数据量相差不多,然后设置拆分策略为禁止拆分,最后导入数据即可。

3)创建表时配置:

已经有自动分区了,为什么还需要预分区?

HBase 在创建表的时候,会自动为表分配一个Region,当一个 Region 达到拆分条件时(shouldSplit 为 true),HBase 中该 Region 将会进行 split,分裂为2个 Region,以此类推。表在进行 split 的时候,会耗费很多的资源,有大量的 io 操作,频繁的分区对 HBase 的性能有很大的影响。

所以,HBase 提供了预分区功能,让用户可以在创建表的时候对表按照一定的规则分区。

假设初始 10 个 Region,那么导入大量数据的时候,就会均衡到 10 个 Region 里面,显然比初始 1 个 Region 要好很多, 合理的预分区可以减少 Region 热点问题,提升写数据的性能和速度,而且也能减少后续的 split 操作 。

首先要明白数据的 RowKey 是如何分布的,然后根据 RowKey 的特点规划要分成多少 Region,每个 Region 的 startKey 和 endKey 是多少,接着就可以预分区了。

比如,RowKey 的前几位字符串都是从 0001~0010 的数字,这样可以分成10个Region:

第一行为第一个 Region 的 stopKey。为什么后面会跟着一个“|“,是因为在ASCII码中,“|“的值是124,大于所有的数字和字母等符号。

shell中建分区表

也可以通过指定 SPLITS_FILE 的值指定分区文件,从文件中读取分区值,文件格式如上述例子所示:

预分区后,可以从 HBase ui 页面观察到:

HBase API 建预分区表

为防止热点问题,同时避免 Region Split 后,部分 Region 不再写数据或者很少写数据。也为了得到更好的并行性,希望有好的 load blance,让每个节点提供的请求处理都是均等的,并且 Region 不要经常 split,因为 split 会使 server 有一段时间的停顿,随机散列加上预分区是比较好的解决方式。

预分区一开始就预建好了一部分 Region,这些 Region 都维护着自已的 start-end keys,再配合上随机散列,写数据能均等地命中这些预建的 Region,就能通过良好的负载,提升并行,大大地提高了性能。

hash + 预分区

在 RowKey 的前面拼接通过 hash 生成的随机字符串,可以生成范围比较随机的 RowKey,可以比较均衡分散到不同的 Region 中,那么就可以解决写热点问题。

假设 RowKey 原本是自增长的 long 型,可以将 RowKey 先进行 hash,加上本身 id ,组成rowkey,这样就生成比较随机的 RowKey 。

那么对于这种方式的 RowKey 设计,如何去进行预分区?

partition + 预分区

partition 顾名思义就是分区式,这种分区有点类似于 mapreduce 中的 partitioner,将区域用长整数作为分区号,每个 Region 管理着相应的区域数据,在 RowKey 生成时,将 id 取模后,然后拼上 id 整体作为 RowKey 。

1. HBase Region 自动拆分策略 2. hbase预分区

Hbase分区

HBase中,表会被划分为1…n个Region,被托管在RegionServer中。Region二个重要的属性:StartKey与 EndKey表示这个Region维护的rowKey范围,当我们要读/写数据时,如果rowKey落在某个start-end key范围内,那么就会定位到目标region并且读/写到相关的数据。 默认地,当我们只是通过HBaseAdmin指定TableDescriptor来创建一张表时,start-end key无边界,region的size越来越大时,大到 一定的阀值,就会找到一个midKey将region一分为二,成为2个region,这个过 程称为分裂(region-split).而midKey则为这二个region的临界

1.总是往最大start-key的region写记录,之前分裂出来的region不会再被写数据,它们都处于半满状态 2.split是比较耗时耗资源

合理设计rowkey 能让各个region 的并发请求 平均分配(趋于均匀) 使IO 效率达到最高 (预分区需要将hbase.hregion.max.filesize设置一个较大的值,默认是10G(0.94.3 ) 也就是说单个region 默认大小是10G)

shell 指明分割点

HexStringSplit指明分割策略,-c 10指明要分割的区域数量,-f指明表中的列族,用“:”分割。

根据文件创建分区并压缩

COMPRESSION 默认值NONE,即不使用压缩 建议采用SNAPPY压缩

官方文档给出的建表提示

TTL 默认是 2147483647 即:Integer.MAX_VALUE 值 大概是68年,这个参数是说明该列族数据的 存活时间,单位是s,超过存过时间的数据将在表中不在显示,待下次major compact的时候再彻底删除数据, 若设置MIN_VERSIONS=》’0’ TTL时间戳过期后,将全部彻底删除该family 下所有的数据,如果MIN_VERSIONS 不等于0 那将保留最新的MIN_VERSIONS个版本的数据,其它的全部删除,比如MIN_VERSIONS=》’1’ 届时将保留一个最新版本的数据,其它版本的数据将不再保存。

VERSIONS 默认是3,这个参数的意思是数据保留三个 版本,如果数据随时都在更新,或老版本的数据无价值,那将此参数设为1 能节约2/3的空间 RegionSplitter提供三个用于预分割的工具:HexStringSplit、SplitAlgorithm、UniformSplit。 HexStringSplit和UniformSplit是两个预定义的静态类,可以直接使用;而SplitAlgorithm是一个接口,需要开发人员自己实现相应的分隔策略。如果是以十六进制字符串作为行键rowkey或者行键rowkey的前缀是十六进制字符串,用HexStringSplit就比较合适;UniformSplit会把行键均匀地分割多个部分,如果行将rowkey是随机的字节数组,用UniformSplit就比较合适;或者开发者根据需要实现分割策略。

原文: https://blog.csdn.net/Nougats/article/details/72723172

HBase写数据的异常问题以及优化

本篇文章来说道说道如何诊断HBase写数据的异常问题以及优化写性能。和读相比,HBase写数据流程倒是显得很简单:数据先顺序写入HLog,再写入对应的缓存Memstore,当Memstore中数据大小达到一定阈值(128M)之后,系统会异步将Memstore中数据flush到HDFS形成小文件。

HBase数据写入通常会遇到两类问题,一类是写性能较差,另一类是数据根本写不进去。这两类问题的切入点也不尽相同,如下图所示:

写性能优化切入点

1. 是否需要写WAL?WAL是否需要同步写入?

优化原理:数据写入流程可以理解为一次顺序写WAL+一次写缓存,通常情况下写缓存延迟很低,因此提升写性能就只能从WAL入手。WAL机制一方面是为了确保数据即使写入缓存丢失也可以恢复,另一方面是为了集群之间异步复制。默认WAL机制开启且使用同步机制写入WAL。首先考虑业务是否需要写WAL,通常情况下大多数业务都会开启WAL机制(默认),但是对于部分业务可能并不特别关心异常情况下部分数据的丢失,而更关心数据写入吞吐量,比如某些推荐业务,这类业务即使丢失一部分用户行为数据可能对推荐结果并不构成很大影响,但是对于写入吞吐量要求很高,不能造成数据队列阻塞。这种场景下可以考虑关闭WAL写入,写入吞吐量可以提升2x~3x。退而求其次,有些业务不能接受不写WAL,但可以接受WAL异步写入,也是可以考虑优化的,通常也会带来1x~2x的性能提升。

优化推荐:根据业务关注点在WAL机制与写入吞吐量之间做出选择

其他注意点:对于使用Increment操作的业务,WAL可以设置关闭,也可以设置异步写入,方法同Put类似。相信大多数Increment操作业务对WAL可能都不是那么敏感~

2. Put是否可以同步批量提交?

优化原理:HBase分别提供了单条put以及批量put的API接口,使用批量put接口可以减少客户端到RegionServer之间的RPC连接数,提高写入性能。另外需要注意的是,批量put请求要么全部成功返回,要么抛出异常。

优化建议:使用批量put进行写入请求

3. Put是否可以异步批量提交?

优化原理:业务如果可以接受异常情况下少量数据丢失的话,还可以使用异步批量提交的方式提交请求。提交分为两阶段执行:用户提交写请求之后,数据会写入客户端缓存,并返回用户写入成功;当客户端缓存达到阈值(默认2M)之后批量提交给RegionServer。需要注意的是,在某些情况下客户端异常的情况下缓存数据有可能丢失。

优化建议:在业务可以接受的情况下开启异步批量提交

使用方式:setAutoFlush(false)

4. Region是否太少?

优化原理:当前集群中表的Region个数如果小于RegionServer个数,即Num(Region of Table) 《 Num(RegionServer),可以考虑切分Region并尽可能分布到不同RegionServer来提高系统请求并发度,如果Num(Region of Table) 》 Num(RegionServer),再增加Region个数效果并不明显。

优化建议:在Num(Region of Table) 《 Num(RegionServer)的场景下切分部分请求负载高的Region并迁移到其他RegionServer;

5. 写入请求是否不均衡?

优化原理:另一个需要考虑的问题是写入请求是否均衡,如果不均衡,一方面会导致系统并发度较低,另一方面也有可能造成部分节点负载很高,进而影响其他业务。分布式系统中特别害怕一个节点负载很高的情况,一个节点负载很高可能会拖慢整个集群,这是因为很多业务会使用Mutli批量提交读写请求,一旦其中一部分请求落到该节点无法得到及时响应,就会导致整个批量请求超时。因此不怕节点宕掉,就怕节点奄奄一息!

优化建议:检查RowKey设计以及预分区策略,保证写入请求均衡。

6. 写入KeyValue数据是否太大?

KeyValue大小对写入性能的影响巨大,一旦遇到写入性能比较差的情况,需要考虑是否由于写入KeyValue数据太大导致。KeyValue大小对写入性能影响曲线图如下:

图中横坐标是写入的一行数据(每行数据10列)大小,左纵坐标是写入吞吐量,右坐标是写入平均延迟(ms)。可以看出随着单行数据大小不断变大,写入吞吐量急剧下降,写入延迟在100K之后急剧增大。

说到这里,有必要和大家分享两起在生产线环境因为业务KeyValue较大导致的严重问题,一起是因为大字段业务写入导致其他业务吞吐量急剧下降,另一起是因为大字段业务scan导致RegionServer宕机。

案件一:大字段写入导致其他业务吞吐量急剧下降

部分业务反馈集群写入忽然变慢、数据开始堆积的情况,查看集群表级别的数据读写QPS监控,发现问题的第一个关键点:业务A开始写入之后整个集群其他部分业务写入QPS都几乎断崖式下跌,初步怀疑黑手就是业务A。

下图是当时业务A的写入QPS(事后发现脑残忘了截取其他表QPS断崖式下跌的惨象),但是第一感觉是QPS并不高啊,凭什么去影响别人!

于是就继续查看其他监控信息,首先确认系统资源(主要是IO)并没有到达瓶颈,其次确认了写入的均衡性,直至看到下图,才追踪到影响其他业务写入的第二个关键点:RegionServer的handler(配置150)被残暴耗尽:

对比上面两张图,是不是发现出奇的一致,那就可以基本确认是由于该业务写入导致这台RegionServer的handler被耗尽,进而其他业务拿不到handler,自然写不进去。那问题来了,为什么会这样?正常情况下handler在处理完客户端请求之后会立马释放,唯一的解释是这些请求的延迟实在太大。

试想,我们去汉堡店排队买汉堡,有150个窗口服务,正常情况下大家买一个很快,这样150个窗口可能只需要50个服务。假设忽然来了一批大汉,要定制超大汉堡,好了,所有的窗口都工作起来,而且因为大汉堡不好制作导致服务很慢,这样必然会导致其他排队的用户长时间等待,直至超时。

可回头一想这可是写请求啊,怎么会有这么大的请求延迟!和业务方沟通之后确认该表主要存储语料库文档信息,都是平均100K左右的数据,是不是已经猜到了结果,没错,就是因为这个业务KeyValue太大导致。KeyValue太大会导致HLog文件写入频繁切换、flush以及compaction频繁触发,写入性能急剧下降。

目前针对这种较大KeyValue写入性能较差的问题还没有直接的解决方案,好在社区已经意识到这个问题,在接下来即将发布的下一个大版本HBase 2.0.0版本会针对该问题进行深入优化,详见 HBase MOB ,优化后用户使用HBase存储文档、图片等二进制数据都会有极佳的性能体验。

案件二:大字段scan导致RegionServer宕机

案件现场:有段时间有个0.98集群的RegionServer经常频繁宕机,查看日志是由于”java.lang.OutOfMemoryError: Requested array size exceeds VM limit”,如下图所示:

原因分析:通过查看源码以及相关文档,确认该异常发生在scan结果数据回传给客户端时由于数据量太大导致申请的array大小超过JVM规定的最大值( Interge.Max_Value-2)。造成该异常的两种最常见原因分别是:

表列太宽(几十万列或者上百万列),并且scan返回没有对列数量做任何限制,导致一行数据就可能因为包含大量列而数据超过array大小阈值

KeyValue太大,并且scan返回没有对返回结果大小做任何限制,导致返回数据结果大小超过array大小阈值

有的童鞋就要提问啦,说如果已经对返回结果大小做了限制,在表列太宽的情况下是不是就可以不对列数量做限制呢。这里需要澄清一下,如果不对列数据做限制,数据总是一行一行返回的,即使一行数据大小大于设置的返回结果限制大小,也会返回完整的一行数据。在这种情况下,如果这一行数据已经超过array大小阈值,也会触发OOM异常。

解决方案:目前针对该异常有两种解决方案,其一是升级集群到1.0,问题都解决了。其二是要求客户端访问的时候对返回结果大小做限制(scan.setMaxResultSize(2*1024*1024))、并且对列数量做限制(scan.setBatch(100)),当然,0.98.13版本以后也可以对返回结果大小在服务器端进行限制,设置参数hbase.server.scanner.max.result.size即可

写异常问题检查点

上述几点主要针对写性能优化进行了介绍,除此之外,在一些情况下还会出现写异常,一旦发生需要考虑下面两种情况(GC引起的不做介绍):

Memstore设置是否会触发Region级别或者RegionServer级别flush操作?

问题解析:以RegionServer级别flush进行解析,HBase设定一旦整个RegionServer上所有Memstore占用内存大小总和大于配置文件中upperlimit时,系统就会执行RegionServer级别flush,flush算法会首先按照Region大小进行排序,再按照该顺序依次进行flush,直至总Memstore大小低至lowerlimit。这种flush通常会block较长时间,在日志中会发现“ Memstore is above high water mark and block 7452 ms”,表示这次flush将会阻塞7s左右。

问题检查点:

Region规模与Memstore总大小设置是否合理?如果RegionServer上Region较多,而Memstore总大小设置的很小(JVM设置较小或者upper.limit设置较小),就会触发RegionServer级别flush。集群规划相关内容可以参考文章《》

列族是否设置过多,通常情况下表列族建议设置在1~3个之间,最好一个。如果设置过多,会导致一个Region中包含很多Memstore,导致更容易触到高水位upperlimit

Store中HFile数量是否大于配置参数blockingStoreFile?

问题解析:对于数据写入很快的集群,还需要特别关注一个参数:hbase.hstore.blockingStoreFiles,此参数表示如果当前hstore中文件数大于该值,系统将会强制执行compaction操作进行文件合并,合并的过程会阻塞整个hstore的写入。通常情况下该场景发生在数据写入很快的情况下,在日志中可以发现” Waited 3722ms on a compaction to clean up ‘too many store files “

问题检查点:

参数设置是否合理? hbase.hstore.compactionThreshold表示启动compaction的最低阈值,该值不能太大,否则会积累太多文件,一般建议设置为5~8左右。 hbase.hstore.blockingStoreFiles默认设置为7,可以适当调大一些。

写性能还能再提高么?

上文已经从写性能优化以及写异常诊断两个方面对HBase中数据写入可能的问题进行了详细的解释,相信在0.98版本的基础上对写入来说已经是最好的解决方案了。但是有些业务可能依然觉得不够快,毕竟”更快”是所有存储系统活着的动力,那还有提高空间吗?当然,接下来简单介绍HBase之后版本对写性能优化的两点核心改进:

Utilize Flash storage for WAL(HBASE-12848)

这个特性意味着可以将WAL单独置于SSD上,这样即使在默认情况下(WALSync),写性能也会有很大的提升。需要注意的是,该特性建立在HDFS 2.6.0+的基础上,HDFS以前版本不支持该特性。具体可以参考官方jira

什么是hbase中的散列和预分区

个人理解,hbase中的预分区,是为了防止要插入的数据的rowkey设计不太合理导致,数据热写(集中写到某一台或者几台机器上)给服务器造成太大压力以及更严重后果,所以在建表的时候,根据已知的rowkey规则创建出来部分预分区以防止在热写过程中频繁的split等。所以rowkey设计应该考虑到这个问题,将数据散列到hbase集群的各个机器上,均分压力。

hbase scan的startRow和endRow

举一个场景,安全领域的溯源分析,查询维度包括ip,时间戳,端口,协议,可能根据前两的维度的一个或者几个进行原始日志查询,我们可以把原始日志存储到hbase中,而前面提到的几个维度可以分别作为key的一部分。

首先我们应该考虑的是rowkey的设置,第一:散列或者反转,保证数据会随机分布到不同的region当中。第二:预分区,先对数据做一个基本的统计,比如我们预分十个区,我们可以统计一下每个区的startrow和endrow,这样保证每个区的数据相当,另外这样的好处是当我们根据rowkey查询的时候,可以保证直接定位到某个分区。我们线上的数据就是采用的第二种方式。

然后我们应该考虑rowkey的组成。分两种情况,第一种情况:维度不是特别多,我们完全可以把各个维度分别作为rowkey的一部分,比如上文提到的需求,就是采用的这种方式,因为一共四个维度,相对来说比较少。第二种情况:维度过多,如果都作为rowkey的一部分的话长度太大,此时建议考虑二级索引,举个例子:比如对于上面提到的四个维度,如果现在进行扩展,ip,端口,协议需要定位到源和目的,这样的话,整个维度提升到了七个,此时就建议采用二级索引。

目前我们已经确定了hbase存储,并且采用预分区的方式并且采用rowkey进行过滤查询,那么现在考虑rowkey的设计。从技术角度考虑,预分区的方式时间戳不能作为第一部分,这样一定会出现数据倾斜的现象;从业务角度考虑,我们定位日志的时候,首先需要定位ip,然后是端口,最后才是协议,也就是说我们的用户去定位日志的时候,如果定位到端口,那必须先定位ip,如果定位协议的话,必须先定位ip和端口。

综上所述,我们的rowkey设计为ip+timestamp+port+prot

设计搞定之后,我们再考虑查询的问题。我们知道对于hbase的查询,最快的方式就是get,这样的话,可以迅速定位到一条数据。而get查询其实就是scan的特殊情况,只是startRow和endRow一样。所以此时我们可以采用scan+startRow+endRow的方式进行操作。

e.g

这样的话就可以吧该范围的数据查出来,当然我们可以再在内存中进行过滤

当着startRow和endRow需要注意一些情况。

请参考:https://www.cnblogs.com/llphhl/p/5719119.html

hbase建很多空表对集群有影响么

hbase建很多空表对集群有影响。HBase每张表在底层存储上是由至少一个Region组成,Region实际上就是HBase表的分区。HBase新建一张表时默认Region即分区的数量为1,一般在生产环境中我们都会手动给Table提前做预分区,使用合适的分区策略创建好一定数量的分区并使分区均匀分布不同regionserver上。一个分区在达到一定大小时会自动Split,一分为二。

hbase预分区与region切割的关系

一张表预分区N个,那就是一开始就设定了N个region;hbase.hregion.max.filesize 设定的region大小,超过了就会split,就会增加一个region,对预分区没什么影响。一张表假如不预分区,那么数据超过region最大值才会拆分,比如你1天10G数据,设定5G才split,两天内写数据都写在一个region里,没有分布式效果,改region就是热点。预设分区一般配合rowkey设计解决热点,例如预分区5个,rowkey可设置前最A、B、C、D、E,程序里面随机的加其中的一个前缀,那么就会随机插入到各个region中,但是一般又会和业务系统需求有些矛盾。例如rowkey按时间戳字符串加盐,那么就只有一各个region scan再合起来统计会比较快,直接scan “A20181111”到“D20181128”很慢很麻,还有按用户ID、手机号等等做rowkey加盐或者hash散列可能都会在设计上有热点和业务需求的矛盾点。

hbase 的数据存储及Region变化(flush compaction spilt)和性能调优

1. 对表做预分区处理(即在建表时指定Region数量和拆分边界);

2.配置hbase.hregion.max.filesize为50GB

以fileServer为例,在使用默认的split策略--IncreasingToUpperBoundRegionSplitPolicy 的情况下,16个预分区Region, 则单个Resion容量达到 min(32,50),即32GB时分裂。

3.修改Linux最大文件句柄数

因为hbase是以文件的形式存储数据,最大文件句柄数影响着hbase的并发量。

用root权限修改/etc/security/limits.conf文件,增加以下内容(前面的*不能忽略):*              soft    nproc          10240

*              hard    nproc          10240

*              soft    nofile          10240

*              hard    nofile          10240 /etc/pam.d/common-session,加入一行session required  pam_limits.so /etc/profile,加入ulimit -SHn 51200重新登陆,生效

4.HRegionServer挂掉异常和解决:

is not online on......常规解决方案:

  删除zk中hbase的缓存

  重启hbase

使用上述解决方案后本次异常依旧存在,并且HMaster和HRegionServer都不断的自动挂掉。

HMaster报错:解决方案:

新增配置(看情况决定使用不使用,建议在HMaster不能启动时排除错误使用)(让启动hbase时只让HMaster去进行日志split,缺点是恢复数据时候速度慢):《property》

《name》hbase.master.distributed.log.splitting《/name》

《value》false《/value》

《/property》

   删除WAL文件(会丢数据):

6. RPC请求的最大线程数

hbase.regionserver.handler.count  默认是10,在服务器测试时建议设置到50(经测试在单个Region Server时无用,单个RegionServer 最多在6个线程put时保持稳定)

7.日志分割(hbase出错后恢复数据)

MemStore中大量更新丢失时,对数据进行恢复时会做日志分割

hbase.regionserver.hlog.splitlog.writer.threads 日志分割的线程数, 默认为3 ,建议设定为10

8.Region Server频繁掉线

出现Hbase Region Server频繁掉线的情况,表现为在多线程put的情况下,忽然Hbase Region Server掉线

猜测是GC或者split过程中没有及时和ZK通信,导致与ZK连接时间超时,zk返回dead region到master,当Hbase Region恢复正常后,找不到wal,产生如下报错。 zookeeper.session.timeout :默认值是3分钟

但是 hbase regionserver和zookeeper的timeout不是单方面决定的,是取决于hbase的zookeeper.session.timeout和zookeeper的MaxSessionTimeout中的最小值

配置hbase: zookeeper.session.timeout

600000配置zookeeper:tickTime=300009.内存及GC优化

在测试的过程中依旧出现Hbase Region Server掉线的情况,报错如下

2021-02-0318:49:14,091INFO

2021-02-0318:49:14,091WARNwal.MetricsWAL: regionserver/botsc/192.168.0.107:16020.append-pool5-t1 took1953ms appending an edit to wal; len~=109

2021-02-0318:49:14,106ERRORwal.FSHLog:Errorsyncing, request close of WAL

java.io .IOException:io.grpc.StatusRuntimeException: CANCELLED: Failed to stream message

    at seaweed.hdfs.SeaweedOutputStream.flushWrittenBytesToServiceInternal(SeaweedOutputStream.java:78)

    at seaweed.hdfs.SeaweedOutputStream.flushWrittenBytesToServiceAsync(SeaweedOutputStream.java:263)

    at seaweed.hdfs.SeaweedOutputStream.flushInternalAsync(SeaweedOutputStream.java:243)

    at seaweed.hdfs.SeaweedOutputStream.flush(SeaweedOutputStream.java:129)

at java.io .FilterOutputStream.flush(FilterOutputStream.java:140)

at java.io .DataOutputStream.flush(DataOutputStream.java:123)

    at org.apache.hadoop.hbase.regionserver.wal.ProtobufLogWriter.sync(ProtobufLogWriter.java:170)

    at org.apache.hadoop.hbase.regionserver.wal.FSHLog$SyncRunner.run(FSHLog.java:1286)

    at java.lang.Thread.run(Thread.java:748)修改hbase的配置文件hbase-env.sh,GC优化如下:export HBASE_HEAPSIZE=21384

export master_heapsize=8292

export regionserver_heapsize=21384

export HBASE_OPTS=“$HBASE_OPTS -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseParNewGC -XX:ParallelGCThreads=6“

export HBASE_MASTER_OPTS=“$HBASE_MASTER_OPTS $HBASE_JMX_BASE -Xmx8g -Xms8g -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70“

export HBASE_REGIONSERVER_OPTS=“$HBASE_REGIONSERVER_OPTS $HBASE_JMX_BASE -Xmx20g -Xms20g -Xmn1g -XX:+UseParNewGC

-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70“

Hbase读写原理

不同列族分别存在不同的文件夹里。

与MySQL比较

首先Hbase是依赖于HDFS和zookeeper的。 Zookeeper分担了Hmaster的一部分功能,客户端进行DML语句的时候,都是先跟ZK交互。 RegionServer管理了很多的Region(表),RegionServer里面的WAL(HLog)是预写入日志,功能是防止内存中的数据没有来的及落盘时丢失。在Region里面管理的Store管理的是列族,Store里面有Mem Store(内存),Flush之后,删除内存中的数据,同时写入文件StoreFile Hfile,Hfile 其实是在DataNode里面的。

Hbase的读比写慢。 Hbase命名空间下有一张元数据表meta表和namespace表。meta表里面保存了要操作的表所在的位置等元数据。 (1)首先客户端向zk请求元数据表所在的RegionServer,zk返回给客户端meta表所在的regionServer。 (2)然后客户端再去对应的RegionServer查找meta表,找到真正要操作的表所在的regionServer,同时把meta表的信息缓存下来,加快后续的查询。 (3)然后客户端再向目标表所在的RegionServer发送put请求。先把数据写到Hlog里面,再写到内存MemStore,数据会在内存排序,然后向客户端发送ack,到这里对于客户端来说写数据已经结束了。再等到MemStore的刷写时机后,将数据刷写到Hfile.

注:meta表所在的位置信息保存在zk的meta-region-server节点上,客户端首先就是在这个节点上差询meta表所在的RegionServer。meta表里面的信息就是表与其对应的RegionServer的信息

这个stu表可能不止一条,因为stu表可能数据量大了之后根据RowKey进行了切分,并且可能会在不同的机器上。

不同的列族是在不同的文件夹。 MemStore刷写时机:

全局的MemStore的容量,默认是堆内存的40%。这个容量值会触发flush操作,所有的MemStore都要刷写,flush操作会阻塞读写操作。 会刷写并阻塞到到MemStore大小降到它的最大容量的95%

WAL日志的刷写时机: 可以设置日志的大小和数量,当达到一定数量,刷写到HDFS

(1)从zk找meta表所在的RegionServer (2)从上述RegionServer里的meta表里找目标表所在的RegionServer,同时把meta表缓存,加速后面的查询。 (3)向目标表所在的RegionServer发送get请求。可以从block Cache,MemStore还有StoreFile里面查,具体从哪查根据时间戳,查时间戳大的,具体就都查然后merge取最新。 RegionServer里面有block Cache可以缓存磁盘的数据,加速查询。如果block Cache里面有,就将缓存和MemStore的数据merge然后取最新时间戳,没有就是把磁盘读的和MemStore里面的合并。所以hbase大多数读要走磁盘,所以读很慢。

每次刷写会生成新的Hfile,Hfile很小并且数量多的时候会影响查询的速度。所以要进行合并。合并分为minor Compaction和major Compaction

minor Compaction将临近的若干较小的Hfile合并成一个较大的Hfile,不会清理过期和删除的数据,major Compaction会将一个Store里面的所有Hfile合并成一个大的Hfile,并且会清理掉过期和删除的数据。

数据的读写可以不依赖Hmaster,只需要指定zookeeper,但是Hmaster负责region调度的元数据 但是DDL语言是要有Hmaster的

Flush和major Compact (1)flush在同一个内存中清除过期或删除(删除标记也是一行数据)的数据,但是如果数据不同的版本分布在不同的memStroe,就不能清除。删除的标记在flush之后不会被删,但在后面的major compaction会把删除标记删除掉。 (2)major compaction 会清除过期或删除的数据。

默认情况下,每个Table起初只有一个Region,随着数据的不断写入,Region会自动拆分,两个子Region开始都会在一个Regionserver里面,但是出于负载均衡的考虑,Hmaster有可能会将某个Region传给其他的RegionServer。 Split的时机: (1)当一个Region中的某个Store下的StoreFile的总大小查过某个值,由参数hbase.hregion.max.filesize设定(默认10g),该Region就会按照RowKey进行拆分。 (2)在新版本中这个值是Min(R^2*“hbase.hregion.memStore.flush.size(128M)“,“hbase.hregion.max.filesize“),R是当前RegionServer中属于该Table的Region个数。分region是按照RowKey切分的。这会导致数据倾斜,就是因为切分的阈值在变化,导致切分之后的region数据量不均匀,导致热点的问题。所以在建表的时候要做预分区,就是用RowKey规划好多少个region,不让hbase自己的切分逻辑切分。 官方建议只用一个列族,防止不同的列族之间数据不均匀,单一列族数据量增多,导致全局的flush,数据量小的列族也要flush,这样会形成很多小的storeFile。

delete操作: (1)设置RowKey:打的删除标记是deleteFamily,删除多个版本 (2)设置RowKey+Family:打的标记是deleteFamily,删除多个版本 (3)设置RowKey+family+column:有addColumn()和addColumns().addColumn是删除最新的版本或者删除指定时间戳的版本,删除标记是delete标记。addColumns是删除所有的版本或者删除指定时间戳或之前的版本,删除标记是deleteColumn Delete的操作其实也是put操作,put的是删除的标记。

在Hbase中HMaster负责监控HRegionServer的生命周期,均衡RegionServer的负载,如果HMaster挂掉了,那个整个Hbase集群将处于不健康的状态,并且此时的工作状态不会维持太久。所以Hbase支持对HMaster的高可用配置。 在Hbase的conf目录下新建backup-masters文件,vim加入备份Master,比如slave01,slave02.在把文件分发到各个slave里,然后再启动hbase 就能实现HMaster的高可用了。

每一个region维护着StartRow和EndRow,如果加入的数据符合某个region维护的RowKey范围,则该数据交给这个region维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高Hbase性能。 (1)手动设定预分区

手动设置RowKey分了5个region

(2)生成16进制序列预分区

(3)按照文件中设置的规则预分区 创建split.txt

然后执行

这里如果文件里面给的分区键不是按照顺序的,hbase会先帮我们把键排序,然后按照键来分区。 (4)使用JavaAPI预分区 admin的创建表的方法有多个重载,可以只传表的描述,也可以加入分区的信息。admin.createTable 规划分区要考虑未来数据量和机器的规模。虽然提前做了分区,但是最后如果分区大于了10G,还是会触发split。假设一台机器有100G磁盘,那么预分区尽量大于10个,这样就能避免预分区之后又触发了大于10G的split。

(1)希望数据能够尽量均匀的分配在多个分区里面(散列性)。 (2)唯一性 (3)长度原则(生产环境70到100位) 常见的设计方案: (1)生产随机数、hash、散列值 (2)字符串反转 (3)字符串拼接

电信项目: 一次通话的记录:13112341233-》18998768771 2018-12-12 12:12:21 568 假设分300个区 分区键怎么设计: (299个键) 000| 001| ... 298| RowKey的前面一般会拼上000_,001_,...,298_ 这样做的好处是,根据前三位就能知道哪个分区。 (1)我们希望手机号尽量分布在不同的分区,但是相同的手机号数据集中在同一个分区,这样方便查询某个用户的通话信息。000_13112341233 (2)因为每个人通话的需求不同,也希望把同一个人的通话记录也分布在不同的分区里面。000_13112341233_2019-12-12 哈希取余:%299 假设要查询某用户2019年2月的通话记录,可以用13112341234 201902做startRowkey,13112341234 201903做endRowKey

微博。 1、需求 (1)微博内容的浏览 (2)用户社交:关注用户,取关用户 (3)拉取关注人的微博用户 2、设计表 (1)微博内容表Content 行键:用户id+时间戳 (2)用户关系表 因为正常情况一个用户的粉丝和关注都不多,可以用一行存储关注和粉丝的情况。 行键:用户id (3)初始化页面的表(显示关注的人的最近三条微博)



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有